package wxcloud

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/mars/internal/pkg/cache"
	"github.com/mars/internal/pkg/options"
)

const (
	// 微信小程序获取access_token的API地址
	accessTokenURL = "https://api.weixin.qq.com/cgi-bin/token"
	// cache组名
	tokenCacheGroup = "wxcloud_token"
	// cache key
	tokenCacheKey = "wxcloud_access_token"
)

// AccessTokenResponse 微信API返回的token响应结构
type AccessTokenResponse struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int64  `json:"expires_in"`
	ErrCode     int    `json:"errcode"`
	ErrMsg      string `json:"errmsg"`
}

// TokenInfo 缓存的token信息
type TokenInfo struct {
	Token     string    `json:"token"`
	ExpiresAt time.Time `json:"expires_at"`
}

type WxCloudClient struct {
	httpClient *http.Client
	appId      string
	appSecret  string
	envId      string
	cache      *cache.Group
}

func NewWxCloudClient(opts *options.WxCloudOptions, client *http.Client) *WxCloudClient {
	if client == nil {
		client = &http.Client{
			Timeout: 30 * time.Second,
		}
	}

	wxClient := &WxCloudClient{
		httpClient: client,
		appId:      opts.AppId,
		appSecret:  opts.AppSecret,
		envId:      opts.EnvId,
	}

	// 创建cache group，用于缓存token
	cacheGroup, err := cache.NewGroup(tokenCacheGroup, cache.GetterFunc(wxClient.tokenGetter))
	if err != nil {
		// 如果创建失败，尝试获取已存在的group
		cacheGroup = cache.GetGroup(tokenCacheGroup)
	}
	wxClient.cache = cacheGroup

	return wxClient
}

// AccessToken 获取access_token，支持缓存和自动刷新
func (w *WxCloudClient) AccessToken(ctx context.Context) (string, error) {
	if w.cache == nil {
		return "", fmt.Errorf("cache not initialized")
	}

	// 从缓存获取token
	val, err := w.cache.Get(ctx, tokenCacheKey)
	if err != nil {
		return "", fmt.Errorf("failed to get token from cache: %w", err)
	}

	tokenInfo, ok := val.(*TokenInfo)
	if !ok {
		return "", fmt.Errorf("invalid token info type in cache")
	}

	// 检查token是否过期
	if time.Now().After(tokenInfo.ExpiresAt) {
		// token已过期，需要重新获取
		return w.refreshToken(ctx)
	}

	return tokenInfo.Token, nil
}

// tokenGetter 实现cache.Getter接口，用于获取token
func (w *WxCloudClient) tokenGetter(ctx context.Context, key string) (interface{}, error) {
	if key != tokenCacheKey {
		return nil, cache.ErrNoValue
	}

	// 从微信API获取新的token
	token, expiresIn, err := w.fetchAccessToken(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to fetch access token: %w", err)
	}

	// 计算过期时间，提前5分钟刷新
	expiresAt := time.Now().Add(time.Duration(expiresIn-300) * time.Second) // 300秒 = 5分钟

	tokenInfo := &TokenInfo{
		Token:     token,
		ExpiresAt: expiresAt,
	}

	return tokenInfo, nil
}

// refreshToken 刷新token
func (w *WxCloudClient) refreshToken(ctx context.Context) (string, error) {
	// 使cache失效，强制重新获取
	w.InvalidateToken()

	// 重新获取token
	return w.AccessToken(ctx)
}

// fetchAccessToken 从微信API获取access_token
func (w *WxCloudClient) fetchAccessToken(ctx context.Context) (string, int64, error) {
	// 构建请求URL
	url := fmt.Sprintf("%s?grant_type=client_credential&appid=%s&secret=%s",
		accessTokenURL, w.appId, w.appSecret)

	// 创建HTTP请求
	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return "", 0, fmt.Errorf("failed to create request: %w", err)
	}

	// 发送HTTP请求
	resp, err := w.httpClient.Do(req)
	if err != nil {
		return "", 0, fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	// 检查HTTP状态码
	if resp.StatusCode != http.StatusOK {
		return "", 0, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	// 解析响应
	var tokenResp AccessTokenResponse
	if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
		return "", 0, fmt.Errorf("failed to decode response: %w", err)
	}

	// 检查微信API错误码
	if tokenResp.ErrCode != 0 {
		return "", 0, fmt.Errorf("wechat api error: code=%d, msg=%s",
			tokenResp.ErrCode, tokenResp.ErrMsg)
	}

	// 验证响应数据
	if tokenResp.AccessToken == "" {
		return "", 0, fmt.Errorf("empty access token in response")
	}

	return tokenResp.AccessToken, tokenResp.ExpiresIn, nil
}

// IsTokenValid 检查当前缓存的token是否有效
func (w *WxCloudClient) IsTokenValid(ctx context.Context) bool {
	if w.cache == nil {
		return false
	}

	val, err := w.cache.Get(ctx, tokenCacheKey)
	if err != nil {
		return false
	}

	tokenInfo, ok := val.(*TokenInfo)
	if !ok {
		return false
	}

	return time.Now().Before(tokenInfo.ExpiresAt)
}

// InvalidateToken 手动使token失效，下次调用时会重新获取
func (w *WxCloudClient) InvalidateToken() {
	if w.cache == nil {
		return
	}

	// 通过设置nil值来使缓存失效
	w.cache.Apply(context.Background(), tokenCacheKey, nil)
}

// GetTokenExpiresAt 获取token的过期时间
func (w *WxCloudClient) GetTokenExpiresAt(ctx context.Context) (time.Time, bool) {
	if w.cache == nil {
		return time.Time{}, false
	}

	val, err := w.cache.Get(ctx, tokenCacheKey)
	if err != nil {
		return time.Time{}, false
	}

	tokenInfo, ok := val.(*TokenInfo)
	if !ok {
		return time.Time{}, false
	}

	return tokenInfo.ExpiresAt, true
}

// EnvId 获取微信云环境ID
func (w *WxCloudClient) EnvId() string {
	return w.envId
}
